package testview.zhen.com.myapplication.asm_demo


import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Type
import org.objectweb.asm.commons.AdviceAdapter


/**
 * @author :create by ldr
 * time:2022/10/27 17:42
 * @descript:
 */
class MyMethodVisitor(methodVisitor:MethodVisitor) : MethodVisitor(Opcodes.ASM9,methodVisitor) {
    override fun visitCode() {
        super.visitCode()
        mv.visitCode();
        mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        mv.visitLdcInsn("getAge");
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
    }
}


class ChangeAdapter(
    api: Int,
    mv: MethodVisitor?,
    access: Int,
    name: String?,
    desc: String?
) :
    AdviceAdapter(api, mv, access, name, desc) {
    private var startTimeId = -1
    private var methodName: String? = null
    protected override fun onMethodEnter() {
        super.onMethodEnter()
        startTimeId = newLocal(Type.LONG_TYPE)
        mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false)
        mv.visitIntInsn(LSTORE, startTimeId)
    }

    protected override fun onMethodExit(opcode: Int) {
        super.onMethodExit(opcode)
        val durationId: Int = newLocal(Type.LONG_TYPE)
        mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false)
        mv.visitVarInsn(LLOAD, startTimeId)
        mv.visitInsn(LSUB)
        mv.visitVarInsn(LSTORE, durationId)
        mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")
        mv.visitTypeInsn(NEW, "java/lang/StringBuilder")
        mv.visitInsn(DUP)
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false)
        mv.visitLdcInsn("The cost time of $methodName is ")
        mv.visitMethodInsn(
            INVOKEVIRTUAL,
            "java/lang/StringBuilder",
            "append",
            "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
            false
        )
        mv.visitVarInsn(LLOAD, durationId)
        mv.visitMethodInsn(
            INVOKEVIRTUAL,
            "java/lang/StringBuilder",
            "append",
            "(J)Ljava/lang/StringBuilder;",
            false
        )
        mv.visitMethodInsn(
            INVOKEVIRTUAL,
            "java/lang/StringBuilder",
            "toString",
            "()Ljava/lang/String;",
            false
        )
        mv.visitMethodInsn(
            INVOKEVIRTUAL,
            "java/io/PrintStream",
            "println",
            "(Ljava/lang/String;)V",
            false
        )
    }

    init {
        methodName = name
    }
}